package com.platform.lxh.netty;

import com.platform.lxh.netty.handler.ServerMessageHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

/**
 * @create: 2021-03-10 20:11
 * @author: xiaoh.luo001@bkjk.com
 * @description:
 **/
public class Server {

    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    //建立tcp网络连接需要三次握手，握手完成之后会进入队列，等待服务器accept，backlog其实就是这样一个连接队列，参数数字表示连接队列大小
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    //单次接收最大socket字节数(linux中默认最小值是256)
                    //SO_SNDBUF和SO_RCVBUF都是读取的文件描述符，而linux中读取此值之后，还会*2 https://linux.die.net/man/7/socket
                    .option(ChannelOption.SO_RCVBUF, 2048)
                    .handler(new LoggingHandler(LogLevel.DEBUG))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 定长机制，用于处理粘包和拆包问题的处理器
                            pipeline.addLast(new LengthFieldBasedFrameDecoder(
                                    // 发送的数据包最大长度
                                    1024,
                                    // 长度域偏移量，指的是长度域位于整个数据包字节数组中的下标
                                    0,
                                    // 长度域自己的字节数长度
                                    4,
                                    //长度域的偏移量矫正。 如果长度域的值，除了包含有效数据域的长度外，还包含了其他域（如长度域自身）长度，那么，就需要进行矫正。矫正的值为：包长 - 长度域的值 – 长度域偏移 – 长度域长
                                    0,
                                    //丢弃的起始字节数。丢弃处于有效数据前面的字节数量。比如前面有 4 个节点的长度域，则它的值为 4。
                                    4));
                            pipeline.addLast(new LengthFieldPrepender(4));
                            // 添加自定义协议消息的编码和解码处理器
                            pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new StringEncoder());
                            // 添加具体的消息处理器
                            pipeline.addLast(new ServerMessageHandler());
                        }
                    });
            // 绑定端口并且进行同步
            ChannelFuture future = bootstrap.bind(8585).sync();
            // 对关闭通道进行监听
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
